import store from '../redux/store'
import { sendDrawingDataToStore } from '../redux/action/freeDrawingActions'
import { resetToRedraw } from '../redux//action/drawActions'
let basic, funcField, paintField
/**
 * 初始化绘制画布
 * @param {HTMLCanvasElement} basicCanv 坐标轴所处画布
 * @param {HTMLCanvasElement} funcFieldCanv 函数,图形所处画布
 * @param {HTMLCanvasElement} paintFieldCanv 绘画所处画布
 */
function drawInit(basicCanv, funcFieldCanv, paintFieldCanv) {
	basic = basicCanv.getContext('2d') // 坐标轴所处画布的上下文
	funcField = funcFieldCanv.getContext('2d') // 函数,图形所处画布的上下文
	paintField = paintFieldCanv.getContext('2d') // 绘画所处画布的上下文
	// 调整宽度
	basic.canvas.width = settings.width
	basic.canvas.height = settings.height
	funcField.canvas.width = settings.width
	funcField.canvas.height = settings.height
	paintField.canvas.width = settings.width
	paintField.canvas.height = settings.height
	// 设置初始线宽
	funcField.lineWidth = 2
	paintField.lineWidth = 2
	paintField.lineCap = 'round'
	paintField.lineJoin = 'round'

	store.subscribe(reDraw)
	// window.paintField = paintField

	xOy()
}

const settings = {
	/**
	 * width : 坐标系的长
	 * height : 坐标系的宽
	 * cols : 坐标系的列的数目(除去xOy)
	 * rows : 坐标系的行的数目(除去xOy)
	 * O : 坐标系的原点
	 */
	// width: Math.floor(window.screen.width/80*0.75)*80,
	width: 1200,
	// height: Math.floor(window.screen.height/80*0.9)*80,
	height: 720,
	// unit: Math.floor((window.screen.width / 80) * 0.25) * 8,
	unit: 40,
	accuracy: 1,
	set cols(num) {
		this.unit = this.width / num // 一旦改动cols，就会改变unit，不变长（还可以有另一种方式：只改长，不变unit）
	},
	set rows(num) {
		this.unit = this.height / num
	},
	get cols() {
		return this.width / this.unit
	},
	get rows() {
		return this.height / this.unit
	},
	O: {
		offsetLeft: 0,
		offsetTop: 0,
		get x() {
			return Math.floor(settings.cols / 2) * settings.unit + this.offsetLeft
		},
		get y() {
			return Math.floor(settings.rows / 2) * settings.unit + this.offsetTop
		},
	},
}

// 解构出原点方便使用
const { O } = settings

// 最终转化的一系列帮助函数 turn 由可读点转化为canvas坐标,back 相反
function turnX(num) {
	return +((O.x + num * settings.unit).toFixed(5) + '')
}
function backX(num) {
	return +(((num - O.x) / settings.unit).toFixed(5) + '')
}
function turnY(num) {
	return +((O.y - num * settings.unit).toFixed(5) + '')
}
function backY(num) {
	return +(((O.y - num) / settings.unit).toFixed(5) + '')
}

// 函数绘制对象的构造函数
// fae: Functional analytic expression
class DrawFunction {
	/**
	 *
	 * @param {String} name 函数名称
	 * @param {Object} style 函数样式
	 * @param {String} fae 函数解析式
	 */
	constructor(name, fae, style) {
		this.globalType = 'func'
		this.name = name
		this.fae = fae
		this.formatFae()
		this.style = style
		this.show = true
	}
	draw() {
		this.formatFae()
		const { width, color } = this.style
		funcField.beginPath()
		funcField.lineWidth = width
		funcField.strokeStyle = color
		const xMax = backX(settings.width)
		const xMin = backX(0)
		const yMin = this.faeFunc(xMin)
		funcField.moveTo(turnX(xMin), turnY(yMin))
		for (let x = turnX(xMin); x <= turnX(xMax); x += settings.accuracy) {
			const y = this.faeFunc(backX(x)) // 执行解析式
			if (y === Infinity || y === -Infinity) {
				// 如果x不能为0（作分母），断开连线
				console.log(turnX(settings.accuracy), turnY(this.faeFunc(backX(settings.accuracy))))
				funcField.moveTo(
					O.x + settings.accuracy,
					turnY(this.faeFunc(backX(O.x + settings.accuracy)))
				)
				continue
			}
			// TODO 不能绘制一些奇怪的函数 y=x^8 y=x^10 ...
			funcField.lineTo(x, Math.floor(turnY(y))) // 连线
		}
		funcField.stroke() // 绘制
	}
	formatFae(fae = this.fae) {
		let tmp = fae.replace(/\^/g, '**').replace(/sin/g, 'Math.sin').replace(/cos/g, 'Math.cos')
		tmp = [...tmp]
		for (let i = 0; i < tmp.length; i++) {
			if ((tmp[i] === 'x' || tmp[i] === '(') && !Number.isNaN(+tmp[i - 1])) {
				tmp[i - 1] += '*'
			}
		}
		this.formatedFae = tmp.join('')
		console.log(tmp, this.formatedFae)
		// eslint-disable-next-line
		this.faeFunc = new Function(
			'x',
			`
			return ${this.formatedFae}
		`
		)
	}
	cloneOneself() {
		const funcObj = new DrawFunction(this.name, this.fae, { ...this.style })
		funcObj.show = this.show
		return funcObj
	}
}
// 函数绘制对象的构造函数
class DrawGraph {
	constructor(name, params, type, style) {
		this.globalType = 'graph'
		this.name = name
		this.params = params
		this.type = type
		this.style = style
		this.show = true
	}
	draw() {
		const { params, type, style } = this
		const { width, color } = style
		funcField.beginPath()
		funcField.lineWidth = width
		funcField.strokeStyle = color
		switch (type) {
			case 0: {
				funcField.arc(
					turnX(params[0]),
					turnY(params[1]),
					params[2] * settings.unit,
					0,
					(360 * Math.PI) / 180
				)
				funcField.stroke()
				break
			}
			case 1: {
				funcField.arc(
					turnX(params[0]),
					turnY(params[1]),
					params[2] * settings.unit,
					(params[3] * Math.PI) / 180,
					(params[4] * Math.PI) / 180
				)
				funcField.stroke()
				break
			}
			default: {
				break
			}
		}
	}
	cloneOneself() {
		const graphObj = new DrawGraph(this.name, this.params, this.type, { ...this.style })
		graphObj.show = this.show
		return graphObj
	}
}

/**
 * 内部存在一个数组，每次绘画时将坐标推入数组，松手后即结束该次绘画
 * 结束绘画的同时，将数组交予Redux store管理
 */
class FreeDrawing {
	constructor(color, width) {
		this.color = color
		this.width = width
		this.startPoint = []
		this.drawingPoint = []
	}
	/**
	 * 开始绘画
	 * @param {Array} point 关于点的坐标的数组
	 */
	startDrawing(point) {
		paintField.beginPath()

		paintField.strokeStyle = this.color
		paintField.lineWidth = this.width
		paintField.moveTo(point[0], point[1])
		this.startPoint = point
	}
	draw(point, needToPush = true) {
		paintField.lineTo(point[0], point[1])
		paintField.stroke()
		needToPush && this.drawingPoint.push(point)
	}
	endDrawing() {
		store.dispatch(sendDrawingDataToStore(this))
	}
}
// 自由绘制橡皮
FreeDrawing.Eraser = class {
	constructor(width) {
		this.width = width
		this.drawingPoint = []
	}
	draw(point, needToPush = true) {
		const { width } = this
		paintField.clearRect(point[0], point[1], width, width)
		needToPush && this.drawingPoint.push(point)
	}
	endDrawing() {
		store.dispatch(sendDrawingDataToStore(this))
	}
}
// 自由绘制清屏
FreeDrawing.Clear = class {
	draw() {
		paintField.clearRect(0, 0, settings.width, settings.height)
	}
}
// 绘制坐标系
function xOy() {
	basic.beginPath()
	basic.fillStyle = '#fff'
	basic.lineWidth = 2
	basic.font = `${settings.unit / 3}px monocase`
	basic.clearRect(0, 0, settings.width, settings.height)
	// 绘制坐标轴以外的线和线边数字
	basic.strokeStyle = '#aaa'
	// (O.offsetLeft % settings.unit) 得到可能存在的不完整格子的偏移，减去单元长度得到最小的最后一个在外绘制的值
	for (
		let i = (O.offsetLeft % settings.unit) - settings.unit;
		i <= settings.width;
		i += settings.unit
	) {
		if (i.toFixed(5) === O.x.toFixed(5)) continue
		basic.moveTo(i, 0)
		basic.lineTo(i, settings.height)
		basic.fillText(
			((i - O.x) / settings.unit).toFixed(0),
			i + settings.unit / 7,
			O.y + (O.offsetTop < settings.height / 2 ? settings.unit / 2 : -settings.unit / 2 + 7)
		)
	}
	for (
		let i = (O.offsetTop % settings.unit) - settings.unit;
		i <= settings.height;
		i += settings.unit
	) {
		if (i.toFixed(5) === O.y.toFixed(5)) continue
		basic.moveTo(0, i)
		basic.lineTo(settings.width, i)
		basic.fillText(
			((O.y - i) / settings.unit).toFixed(0),
			O.x + (O.offsetLeft < settings.width / 2 ? settings.unit / 7 : -settings.unit / 7 - 16),
			i + settings.unit / 2
		)
	}
	basic.stroke()
	// 绘制坐标轴
	basic.beginPath()
	basic.strokeStyle = '#fff'
	// 绘制原点
	basic.arc(O.x, O.y, 5, 0, 180 * Math.PI)
	basic.fill()
	basic.fillText('0', O.x + settings.unit / 7, O.y + settings.unit / 2)
	// 绘制坐标轴的线条
	basic.moveTo(0, O.y)
	basic.lineTo(settings.width, O.y)
	basic.moveTo(O.x, 0)
	basic.lineTo(O.x, settings.height)
	// 绘制箭头
	if (O.y > 0) {
		// 只有一二象限画向上箭头
		// 向上箭头
		basic.moveTo(O.x, 0)
		basic.lineTo(O.x - 0.2 * settings.unit, 0 + 0.2 * settings.unit)
		basic.moveTo(O.x, 0)
		basic.lineTo(O.x + 0.2 * settings.unit, 0 + 0.2 * settings.unit)
	}
	if (O.x < settings.width) {
		// 同上
		// 向右箭头
		basic.moveTo(settings.width, O.y)
		basic.lineTo(settings.width - 0.2 * settings.unit, O.y - 0.2 * settings.unit)
		basic.moveTo(settings.width, O.y)
		basic.lineTo(settings.width - 0.2 * settings.unit, O.y + 0.2 * settings.unit)
	}
	basic.stroke()
	// x y的字体 (去除，挡数字)
	// basic.fillText('x', settings.width - settings.unit * 0.45, O.y + settings.unit * 0.45)
	// basic.fillText('y', O.x - settings.unit * 0.45, settings.unit * 0.45)
	// 每次修改坐标系后都需要重新对齐函数图像
	reDraw(true)
}

// 重新绘制所有的函数,图形
function reDraw(forceUpdate = false) {
	// !store.getState().drawReducer.edited
	// console.log(store.getState().drawReducer.edited, forceUpdate)
	if (!store.getState().drawReducer.toRedraw && !forceUpdate) return
	funcField.clearRect(0, 0, settings.width, settings.height)
	store.getState().drawReducer.drawList.forEach((obj) => obj.show && obj.draw())
	store.dispatch(resetToRedraw())
}

function onlyView(quadrantOrder) {
	// 详情见原理图
	switch (quadrantOrder) {
		case 0: {
			O.offsetLeft = 0
			O.offsetTop = 0
			break
		}
		case 1: {
			O.offsetLeft = -(O.x - O.offsetLeft)
			O.offsetTop = settings.height - (O.y - O.offsetTop)
			break
		}
		case 2: {
			O.offsetLeft = settings.width - (O.x - O.offsetLeft)
			O.offsetTop = settings.height - (O.y - O.offsetTop)
			break
		}
		case 3: {
			O.offsetLeft = settings.width - (O.x - O.offsetLeft)
			O.offsetTop = -(O.y - O.offsetTop)
			break
		}
		case 4: {
			O.offsetLeft = -(O.x - O.offsetLeft)
			O.offsetTop = -(O.y - O.offsetTop)
			break
		}
		default: {
			return
		}
	}
	xOy()
}

function exportImg() {
	basic.drawImage(funcField.canvas, 0, 0)
	basic.drawImage(paintField.canvas, 0, 0)
	const data = basic.canvas.toDataURL('image/png')
	xOy()
	return data
}

function reloadSettings() {
	// 清空画布
	// funcField.clearRect(0, 0, settings.width, settings.height)
	paintField.clearRect(0, 0, settings.width, settings.height)

	xOy()
}

window.exportImg = exportImg

export {
	drawInit,
	onlyView,
	DrawFunction,
	DrawGraph,
	FreeDrawing,
	exportImg,
	settings,
	turnX,
	turnY,
	backX,
	backY,
	reloadSettings,
}
